home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-12-13 | 18.5 KB | 598 lines | [TEXT/KAHL] |
- // routines for working with the image compressor component,
- // this stuff is culled from an earlier sample I did, so I think
- // it is probably better commented than the rest of this stuff!!
- //
- // However, this code was origially developed to work with the Image
- // compression stuff in QuickTime 1.0, and uses the calls that were
- // available then (with a couple of minor exceptions). I may need to modernise
- // this at some point.
- //
- // Nick Thompson, June '94
- //
- // Copyright: © 1994 by Apple Computer, Inc., all rights reserved.
-
- #include <ImageCompression.h>
- #include <QuickTimeComponents.h>
- #include "CompressImageTest.h"
-
- #include "GetQTCompressedPict.h"
-
-
- //--------------------------------------------------------------------------------------
- //
- // Name CheckError
- //
- // Arguments OSErr error the code returned by a toolbox routine
- // Str255 displayString a string to display in the debugger
- //
- // Description ched the code in error, if != noErr, enter the debugger
- //
- // Returns nothing
- //
- // Side Effects may cause execution to terminate
- //
- //--------------------------------------------------------------------------------------
-
- void CheckError(OSErr error, Str255 displayString)
- {
- if (error == noErr) return;
- if (displayString[0] > 0)
- DebugStr(displayString);
- ExitToShell();
- }
-
-
- //--------------------------------------------------------------------------------------
- //
- // Name CompressPixMap
- //
- // Arguments PixMapHandle thePixMap - handle to the pixmap to compress, passed from
- // the offscreen world
- // Rect *theFrame - rectangle describing the bounds of the image
- // SCParams *params - from the user, use the std comression dialog to
- // generate the required compression settings.
- // ComponentInstance ci - the standard component
- //
- // Description This is the business end of the app. Try adding JPEG compression
- // to an app without QuickTime :-> Take an image stored in an offscreen
- // bitmap and apply compression as described by the fields in the
- // SCParams record.
- //
- // Returns On sucessful completion return a handle to the compressed picture..
- //
- // Side Effects using the setting provided by the user compress the image.
- // mat allocate storahe for the image and the image description.
- // Note that this routine may attemt to allocate storage for the compressed
- // image. Whilst the minimum amount required will usually be less
- // than the size of the pixmap passed, for large and or deep images the
- // storage requirement may be large.
- //
- //--------------------------------------------------------------------------------------
-
-
- PicHandle CompressPixMap( PixMapHandle thePixMap,
- Rect *theFrame,
- SCParams *params,
- ComponentInstance ci)
- {
- // local variables
- PicHandle myPic ;
- OpenCPicParams myOpenCPicparams ;
- long maxCompressedSize = 0; // the maximum possible size of the compressed image
- OSErr result = noErr; // the return code of this function
- Handle compressedDataH ; // handle to some store for our compressed image
- Ptr data ; // pointer to the above, 32 bit clean
- ImageDescriptionHandle ImageDescH ; // image description handle
-
- long length ;
- long dummy ;
- GrafPtr currentPort ;
- int index ;
- PixMapHandle myPix ;
-
- // calculate the maximum compression size for the picture.
- // We need to caculate the max possible size for the image,
- // so that we can allocate storage for the call to compress image.
- // The value returned in maxCompressedSize is used to size a Handle.
-
- result = GetMaxCompressionSize(
- thePixMap,
- theFrame,
- params->depth, // pixel depth to compress image at
- params->spatialQuality, // spatial quality to compress image at
- params->theCodecType, // type of codec to use 'jpeg','rpza', etc.
- params->theCodec, // codec to use either in general
- // terms (bestSpeed, bestFidelity,etc.)
- &maxCompressedSize) ; // how big will the pict be??
-
-
- if (result != noErr) {
- CheckError( result, "\pGetMaxCompressionSize failed" );
- return nil;
- }
-
- // Allocate storage for the compressed image. We use the value
- // returned in maxCompressedSize, from a call to GetMaxCompressionSize
- // earlier on.
-
- if(( compressedDataH = NewHandle( maxCompressedSize ) ) == nil )
- result = MemError() ; // why did we get a nil handle back??
-
- if( result != noErr ) {
-
- // handle the error and bail out.
-
- CheckError( result, "\pfailed to allocate memory for the pict" );
- return nil;
-
- }
-
- // move the handle high and lock it down.
-
- MoveHHi( (Handle)compressedDataH ) ;
- HLock( (Handle) compressedDataH ) ;
-
- // make sure we are using a 32bit clean address for the call to comress image
-
- data = StripAddress( *compressedDataH ) ;
-
- // allocate some storage for the image description handle.
-
- if(( ImageDescH = (ImageDescriptionHandle)NewHandle(4) ) == nil )
- result = MemError() ; // why did we get a nil handle back??
-
- if( result != noErr ) {
-
- // free the storage for the data
-
- DisposHandle((Handle)compressedDataH) ;
-
- // handle the error and bail out.
-
- CheckError( result, "\pfailed to allocate memory for the image desc handle" );
- return nil;
-
- }
-
-
- // should *always* lock the images pix map down, before messing with it.
-
- LockPixels( thePixMap ) ;
-
- // Call the image compression manager to compress the image.
- // This is all a bit wierd, the data in the parameter “data”
- // is in a private QuickTime ICM format.
- // To get the compressed pict, we need to open a Picture and
- // decompress the image into it.
-
- result = FCompressImage(
- thePixMap,
- theFrame,
- params->depth, // pixel depth to compress image at
- params->spatialQuality, // spatial quality to compress image at
- params->theCodecType, // type of codec to use 'jpeg','rpza', etc.
- params->theCodec, // codec to use either in general terms (bestSpeed, bestFidelity,etc.)
- nil, // custom clut to use, nil for none
- !codecFlagWasCompressed, // the picture wasn't compressed already (as far as we know)
- 0L, // we have not specified a data unloading proc, so set this to nil
- nil, // no flush proc
- nil, // we dont provide progress information while compressing
- ImageDescH, // storage for the image descriptor
- data ) ; // where to put the compressed image
-
- if( result != noErr ) {
-
- // free the storage for the data
-
- DisposHandle((Handle)compressedDataH) ;
- DisposHandle((Handle)ImageDescH) ;
-
- // did we fail?, handle the failure…
-
- CheckError( result, "\pfailed to compress the picture" );
- return nil;
-
- }
-
- // unlock the pix map.
-
- UnlockPixels( thePixMap) ;
-
- myPix = (PixMapHandle)compressedDataH ;
-
- GetPort( ¤tPort ) ;
-
- myOpenCPicparams.srcRect = *theFrame ;
- myOpenCPicparams.hRes = (**ImageDescH).hRes ;
- myOpenCPicparams.vRes = (**ImageDescH).vRes ;
- myOpenCPicparams.version = -2 ;
-
- myPic = OpenCPicture( &myOpenCPicparams ) ;
-
-
- // DecompressImage will decompress the data in data, and produce a compressed
- // picture. Why call decompresss image here? The data stored in our “data” handle
- // is not of a structure we know about, as it is a private QuickTime data type.
- // Data holds QuickTime compressed information, we need to call decompressImage
- // do decompress the image data, held in quicktime format, into a PICT.
- // one side effect of this call is that the PICT will be a compressed PICT, which
- // is what we want.
-
- result = DecompressImage( data,
- ImageDescH,
- thePixMap,
- theFrame,
- theFrame,
- srcCopy,
- nil ) ;
-
- ClosePicture() ;
-
- HUnlock( (Handle) compressedDataH ) ;
-
- // free the storage for the data
-
- DisposHandle((Handle)compressedDataH) ;
- DisposHandle((Handle)ImageDescH) ;
-
-
- return myPic ; // should be noErr
-
- }
-
- //--------------------------------------------------------------------------------------
- //
- // Name InitialiseSCParams
- //
- // Arguments SCParams *params - ref to an SCParams record to fill in
- //
- // Description fill in the default compression parameters. a real application
- // would read this stuff from the user prefs file
- //
- // Returns nothing
- //
- // Side Effects none, make sure you pass a reference to the record to fill in!!
- //
- //--------------------------------------------------------------------------------------
-
-
- void InitialiseSCParams(SCParams *params)
- {
-
- // Fill in default compression settings. Note that because we
- // don't want motion compression settings (the scShowMotionSettings bit
- // is clear in params->flags), we don't need to initialize params->temporalQuality,
- // params->frameRate, or params->keyFrameRate.
-
- params->flags = 0; // we don't have any special requirements
- params->theCodecType = 'jpeg'; // default to using JPEG compressor
- params->theCodec = anyCodec; // use any of the available JPEG compressors hh
- params->spatialQuality = codecNormalQuality; // default to normal quality
- params->depth = 0; // let the compression dialog pick
- // the best default depth
-
-
- }
-
-
- //--------------------------------------------------------------------------------------
- //
- // Name GetCompressionSettings
- //
- // Arguments DocumentHdl documentHandle - our document data structure
- // SCParams *params - reference to the current compression settings
- // ComponentInstance ci - std compression component
- //
- // Description put up the standard compression dialog, get the compression settings
- // from the user, store them in the SCParams reference.
- //
- // Returns on successful completion noErr, on error an error code
- // indicating the problem
- //
- // Side Effects will cause the std compression dialog to appear.
- //
- //--------------------------------------------------------------------------------------
-
-
- OSErr GetCompressionSettings( WindowPtr theWindow,
- SCParams *params,
- ComponentInstance ci )
- {
- ComponentResult result = noErr;
- Point where;
- GWorldPtr theNewWorld ;
- PixMapHandle offPixMap ;
-
-
- //
- // Tell Standard Compression to use the chosen window
- // for the test image. Standard compression will also
- // accept pict handles for the test image using the
- // SCSetTestImagePictHandle call. A number of file
- // based routines are available for compressing PICT
- // files "in place". The pict file call is often
- // more convenient, but we want to demo offscreen
- // manipulations for this sample
-
- // get the GWorld from the window refcon
- theNewWorld = (GWorldPtr)GetWRefCon ( theWindow );
- offPixMap = GetGWorldPixMap( theNewWorld ) ;
-
- LockPixels( offPixMap ) ;
-
- // SCSetTestImagePixMap is defined in the StdCompression glue
- // this just gets an area of the image to display as a small sample
- // of what compression will look like in the standard compression dialog
-
- result = SCSetTestImagePixMap(
- ci, // our instance of the standard compression component
- offPixMap, // pixMap for source picture
- nil, // we don't have rectangle of interest so use the entire image
- scPreferScalingAndCropping); // scale the test image down until reasonably small
- // and then crop that to the test image box.
-
- UnlockPixels( offPixMap ) ;
-
- if (result != noErr) {
- CheckError( result, "\pSCSetTestImagePixMap failed" );
- return result;
- }
-
-
- // Get compression settings from the user, and provide them with a "Defaults"
- // button. Note that if we were not implementing "Defaults" we could use the
- // shorter call "result = SCGetCompression(ci,¶ms,where);". Giving the user
- // the "Defaults" option is probably not necessary for most applications that
- // would use the Standard Compression dialog, and is only shown as an example
- // of how implement a custom button.
-
-
- where.h = where.v = -2; // already set to (-2,-2) above */
- result = SCGetCompressionExtended(
- (long)ci, // our instance of the standard compression component */
- params, // the default settings */
- where, // (-2,-2) means center dialog on the best device */
- nil, // we don't need to filter any dialog events */
- nil, // our hookProc for handling the custom button */
- 0, // we don't have any data that needs to be passed to our hookProc */
- nil ); // Name of the custom button we want added. If we didn't want a
- // custom button, we'd pass in nil and no button would be shown.
-
-
- return result ;
-
- }
-
- //--------------------------------------------------------------------------------------
- //
- // Name GetOutputFileRef
- //
- // Arguments short *dstPictFRef - a reference to a short file descriptor
- //
- // Description Ask the user for a file to write the image to, using the new
- // style standard file routines. If we aget a file to open from
- // the user, delete any existing file with that name, and create a
- // new file. Open the file and return a reference to it in the
- // parameter dstPictFileRef. Will return an OSErr if a system error
- // occurs.
- //
- // If the user cancels the standard file dialog, then will return
- // userCanceledErr and the file reference will be set to zero.
- //
- // Returns operating system error code if an error happened, else return noErr.
- //
- // Side Effects may cause a dialog to be displayed, may open a file, may delete
- // a preexisting file (on user confirmation)
- //
- //--------------------------------------------------------------------------------------
-
-
- OSErr GetOutputFileRef(short *dstPictFRef ) // GetOutputFileRefNum
- {
- StandardFileReply aPictSFR; // new style Standard file record
- OSErr result ;
- SFTypeList types = { 'PICT',0 }; // we are only interested in PICT files
-
- // check to see if we have the new style standard file package
-
- StandardPutFile( "\pSave PICT as:", "\pCompressed PICT", &aPictSFR);
- if ( aPictSFR.sfGood ) {
-
- // Delete the file of the same name, if one exits,
- // and create a new PICT file.
-
- FSpDelete(&aPictSFR.sfFile);
-
- result = FSpCreate( &aPictSFR.sfFile,'????','PICT', aPictSFR.sfScript);
- if (result != noErr) {
- return result;
- }
-
- // open the data fork for writing
- // ??? need to change the permissions here ???
-
- result = FSpOpenDF( &aPictSFR.sfFile, fsCurPerm, dstPictFRef);
- if (result != noErr) {
- return result;
- }
- }
- else
- return userCanceledErr ;
-
- return noErr ;
- }
-
-
- //--------------------------------------------------------------------------------------
- //
- // Name WritePict
- //
- // Arguments PicHandle myPic - reference to the picture handle
- // short dstPictFRef - file reference, to an open file for writing
- //
- // Description Given a handle to a picture in myPic and a file reference,
- // write the image to the file.
- //
- // Returns On sucessful completion return noErr, else return an error code
- // indicating the reason for the failure.
- //
- // Side Effects may write a byte stream to the file
- //
- //--------------------------------------------------------------------------------------
-
- OSErr WritePict( PicHandle myPic, short dstPictFRef )
- {
- long length ;
- long dummy ;
- GrafPtr currentPort ;
- int index ;
- PixMapHandle myPix ;
- OSErr result ;
-
- // ok, myPic now contains a handle to the compressed picture
-
- HLock( (Handle)(Handle)myPic);
-
- // set up the 512 byte header for a PICT file
- dummy = 0;
-
- for( index = 0; index < ( 512 / 4 ); index ++ ){
- length = 4 ;
-
- if( (result = FSWrite(dstPictFRef, &length, &dummy)) != noErr ) {
- // ErrorAlert( rErrorStrs, eCantWriteFile );
- return result ;
- }
- }
-
- length = GetHandleSize( (Handle)myPic);
-
- if( (result = FSWrite(dstPictFRef, &length, *myPic)) != noErr ) {
- //ErrorAlert( rErrorStrs, eCantWriteFile );
- return result ;
- }
-
- // now get rid of the picture handle
- HUnlock( (Handle) (Handle)myPic ) ;
- KillPicture(myPic);
-
- }
-
-
- //--------------------------------------------------------------------------------------
- //
- // Name DoSaveAs
- //
- // Arguments WindowPtr window - a reference to the window whos content to save
- //
- // Description This is a high level routine that allows the user to save the
- // current window in a compressed format.
- //
- // The principle at work here is to factor the code as far as
- // is possible. This means lots of small functions. why??
- // If we seperate functionality out from user interface code,
- // it becomes easier to provide support for, say, scripting at
- // a later date.
- //
- // Returns nothing. This one is a bit of a do or die routine. The
- // functions that this routine calls will perform error handling.
- //
- // Side Effects none directly, but the routines that this calls may allocate storage
- //
- //--------------------------------------------------------------------------------------
-
- void DoSaveAs( WindowPtr window )
- {
- SCParams params;
- ComponentResult result = noErr;
- Point where;
- ComponentInstance ci = nil;
- SFTypeList typeList;
- SFReply inReply;
- SFReply outReply;
- short srcPictFRef = 0;
- short dstPictFRef = 0;
- PicHandle myPic ;
-
- long maxCompressedSize = 0;
- OSErr theErr;
- Handle compressedDataH ;
- Ptr data ;
- ImageDescriptionHandle ImageDescH ;
- Rect theRect ;
-
-
- GWorldPtr theNewWorld ;
- PixMapHandle offPixMap ;
-
-
- // get the GWorld from the window refcon
- if((theNewWorld = (GWorldPtr)GetWRefCon ( window )) == nil )
- return ;
-
- if((offPixMap = GetGWorldPixMap( theNewWorld )) == nil)
- return ;
-
-
- ci = OpenDefaultComponent(StandardCompressionType,StandardCompressionSubType);
- if (ci == nil)
- return ;
-
- // initialise for standard parameters
-
- InitialiseSCParams(¶ms) ;
-
- // get the user to choose the compression settings, use the
- // standard compression dialog to ensure consistency
- result = GetCompressionSettings( window, ¶ms, ci ) ;
-
- // check to see if the user canceled, or if an error has occurred
- if (result != noErr ) {
-
- if( result != 1 ) { // if user hit cancel, bail but don't generate error.
-
- // This handles the case where an error occurred
- // ErrorAlert( rErrorStrs, eCantGetCompressionSettings );
-
- }
- return ;
-
- }
-
- // get an open a file for writing
- if( (theErr = GetOutputFileRef( &dstPictFRef )) != noErr ) {
- return ;
- }
-
-
- theRect = (**offPixMap).bounds ;
-
- // compress the PixMap here…
- myPic = GetQTCompressedPict( offPixMap, ¶ms ) ;
-
-
- if( myPic == nil ) {
- FSClose(dstPictFRef);
- CloseComponent(ci);
- return ;
- }
- else {
- if(WritePict( myPic, dstPictFRef ) != noErr ) {
- FSClose(dstPictFRef);
- CloseComponent(ci);
- return ;
- }
-
- }
-
- FSClose(dstPictFRef);
-
- // clean up and go…
-
- CloseComponent(ci);
-
- return ;
-
- }
-
-